# -*- coding: utf-8 -*-


class TypeBase:
    def __init__(self, sig):
        self.sig = sig
    def __str__(self):
        return self.sig

class PrimitiveType(TypeBase):
    def __init__(self, sig):
        TypeBase.__init__(self, sig)


Int8 = PrimitiveType('int8_t')
UInt8 = PrimitiveType('uint8_t')
Int16 = PrimitiveType('int16_t')
UInt16 = PrimitiveType('uint16_t')
Int32 = PrimitiveType('int32_t')
UInt32 = PrimitiveType('uint32_t')
Int64 = PrimitiveType('int64_t')
UInt64 = PrimitiveType('uint64_t')
Single = PrimitiveType('float')
Double = PrimitiveType('double')
IntPtr = PrimitiveType('intptr_t')
UIntPtr = PrimitiveType('uintptr_t')
String = PrimitiveType('char*')
WString = PrimitiveType('wchar_t*')
Void = PrimitiveType('void')
Boolean = PrimitiveType('boolean')

Public = 'public'
Protected = 'protected'
Private = 'private'

In = 'in'
Out = 'out'
InOut = 'inout'


register_listener = None
include_handler = None
pragma_handler = None


def set_register_listener(new_listener):
    global register_listener
    register_listener = new_listener

def set_include_handler(new_handler):
    global include_handler
    include_handler = new_handler

def set_pragma_handler(new_handler):
    global pragma_handler
    pragma_handler = new_handler


def register(obj):
    if register_listener:
        register_listener.dispatch(obj)


def find_method(obj, sig):
    return sig in dir(obj)


class AstNodeBase:
    def __init__(self, default_access, **kwargs):
        self.parent = None
        self.access = kwargs['access'] if kwargs.has_key('access') else default_access
        self.desc = kwargs['desc'] if kwargs.has_key('desc') else ''
        self.accept_method = kwargs['accept_method'] if kwargs.has_key('accept_method') else None
        for k in kwargs.keys():
            self.__dict__[k] = kwargs[k]

    def accept(self, visitor):
        if self.accept_method == None:
            return 

        func = getattr(visitor, self.accept_method)
        if func:
            func(self)


class AstOwnerBase(AstNodeBase):
    def __init__(self, id, default_access, *elements, **kwargs):
        AstNodeBase.__init__(self, default_access, **kwargs)
        self.id = id
        self.elements = []
        for e in elements:
            e.parent = self
            self.elements.append(e)

    def members(self, *args):
        for e in get_ast_node(args):
            e.parent = self
            self.elements.append(e)


def get_ast_node(obj):
    if obj == None:
        return Void
    elif isinstance(obj, AstNodeBase) or isinstance(obj, TypeBase):
        return obj
    elif type(obj) in (tuple, list):
        nodes = []
        for e in obj:
            if type(e) in (tuple, list):
                nodes += get_ast_node(e)
            else:
                nodes.append(get_ast_node(e))
        return nodes
    else:
        return obj._ast_node_reference


def Include(__external):
    if include_handler:
        include_handler(__external)


def Pragma(*args):
    if pragma_handler:
        pragma_handler(*args)


class Module(AstOwnerBase):
    def __init__(self, id, *elements, **kwargs):
        AstOwnerBase.__init__(self, id, Public, accept_method='visitModule', *elements, **kwargs)
        register(self)


class Interface(AstOwnerBase, TypeBase):
    def __init__(self, id, *elements, **kwargs):
        TypeBase.__init__(self, id)
        AstOwnerBase.__init__(self, id, Public, accept_method='visitInterface', *elements, **kwargs)
        register(self)


class Struct(AstOwnerBase, TypeBase):
    def __init__(self, id, *elements, **kwargs):
        TypeBase.__init__(self, id)
        AstOwnerBase.__init__(self, id, Public, accept_method='visitStruct', *elements, **kwargs)
        register(self)


class Typedef(AstNodeBase, TypeBase):
    def __init__(self, id, type, **kwargs):
        TypeBase.__init__(self, id)
        AstNodeBase.__init__(self, Public, accept_method='visitTypedef', **kwargs)
        self.id = id
        self.type = type if isinstance(type, TypeBase) else get_ast_node(type)
        register(self)


class Enum(AstNodeBase, TypeBase):
    def __init__(self, id, type=Void, **kwargs):
        TypeBase.__init__(self, id)
        AstNodeBase.__init__(self, Public, accept_method='visitEnum')
        self.id = id
        if not(type in (Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Void)):
            raise TypeError("Iinvalid type specified.")
        self.type = type
        self.values = kwargs
        register(self)


class Operation(AstNodeBase):
    def __init__(self, id, ret, *params, **kwargs):
        AstNodeBase.__init__(self, Public, accept_method='visitOperation', **kwargs)
        self.id = id
        self.ret = ret
        self.params = params
        register(self)


class Param(AstNodeBase):
    def __init__(self, id, type, passing=In, **kwargs):
        AstNodeBase.__init__(self, Public, passing=In, accept_method='visitParam', **kwargs)
        self.id = id
        self.type = type
        self.passing = passing
        register(self)


class Attribute(AstNodeBase):
    def __init__(self, id, type, default_access=Private, accept_method = 'visitAttribute', **kwargs):
        AstNodeBase.__init__(self, default_access=default_access, accept_method=accept_method, **kwargs)
        self.id = id
        self.type = type
        register(self)


class Const(Attribute):
    def __init__(self, name, type, value, **kwargs):
        Attribute.__init__(self, name, type, default_access=Public, accept_method='visitConst', **kwargs)
        self.value = value
        register(self)


class Base():
    def __init__(self, ast):
        self._ast_node_reference = ast


class OwnerBase(Base):
    def __init__(self, ast, *elements):
        Base.__init__(self, ast)
        for e in elements:
            self.__dict__[get_ast_node(e).id] = e

    def declare(self, *args):
        a = get_ast_node(args)
        get_ast_node(self).members(*a)
        for e in a:
            self.__dict__[e.id] = e


class ReferenceType(TypeBase):
    def __init__(self, type):
        self.type = get_ast_node(type)
    def __str__(self):
        if isinstance(self.type, TypeBase):
            rettype = str(self.type)
        else:
            rettype = self.type.id
        return rettype + '*'


def Ref(type):
    return ReferenceType(type)


class RegisterListener:
    def __init__(self): pass

    def dispatch(self, obj):
        if isinstance(obj, Module):
            self.registerModule(obj)
        elif isinstance(obj, Interface):
            self.registerInterface(obj)
        elif isinstance(obj, Struct):
            self.registerStruct(obj)
        elif isinstance(obj, Const):
            self.registerConst(obj)
        elif isinstance(obj, Typedef):
            self.registerTypedef(obj)
        elif isinstance(obj, Enum):
            self.registerEnum(obj)
        elif isinstance(obj, Operation):
            self.registerOperation(obj)
        elif isinstance(obj, Param):
            self.registerParam(obj)
        elif isinstance(obj, Attribute):
            self.registerAttribute(obj)
        else:
            self.registerUnknown(obj)

    def registerUnknown(self, obj): pass
    def registerModule(self, mod): pass
    def registerInterface(self, ifc): pass
    def registerStruct(self, str): pass
    def registerConst(self, con): pass
    def registerTypedef(self, tydf): pass
    def registerEnum(self, enum): pass
    def registerOperation(self, opr): pass
    def registerParam(self, parm): pass
    def registerAttribute(self, attr): pass
